package com.zonta.finance.serivce.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zonta.finance.common.constant.CommonConstant;
import com.zonta.finance.common.dto.PageHelperRequest;
import com.zonta.finance.common.dto.PageInfo;
import com.zonta.finance.common.exception.BizException;
import com.zonta.finance.common.service.TokenService;
import com.zonta.finance.common.utils.DateUtil;
import com.zonta.finance.config.ObjectConvertor;
import com.zonta.finance.constant.RedisKeyConstant;
import com.zonta.finance.domain.dto.AdminDTO;
import com.zonta.finance.domain.form.*;
import com.zonta.finance.domain.po.SysMenu;
import com.zonta.finance.domain.po.SysResource;
import com.zonta.finance.domain.po.SysRole;
import com.zonta.finance.domain.vo.GetRoleDetailVo;
import com.zonta.finance.domain.vo.ListRoleVo;
import com.zonta.finance.domain.vo.MenuDataItemVo;
import com.zonta.finance.mapper.SysRoleMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zonta.finance.serivce.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统角色 服务实现类
 * </p>
 *
 * @author zonta
 * @since 2024-07-25
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
    private final TokenService<AdminDTO> tokenService;
    private final ObjectConvertor objectConvertor;
    private final RedisTemplate<String ,Object> redisTemplate;
    private final ISysMenuService sysMenuService;
    private final ISysRoleBindMenuService sysRoleBindMenuService;
    private final ISysResourceService sysResourceService;
    private final ISysRoleBindResourceService sysRoleBindResourceService;
    /**
     * 新增角色
     * @param form
     * @return
     */
    @Override
    public Boolean createByFrom(CreateSysRoleForm form) {
        SysRole sysRole = new SysRole();
        sysRole.initDefault();
        sysRole.setRoleName(form.getRoleName());
        sysRole.setMemberId(tokenService.getThreadLocalUserId());
        sysRole.setDisable(form.getDisable());
        sysRole.setUpdateMemberId(sysRole.getMemberId());
        boolean flag = save(sysRole);
        if (!flag){
            throw new BizException("保存角色出错");
        }
        return flag ;
    }

    /**
     * 查询角色 （条件）
     * @param form
     * @return
     */
    @Override
    public PageInfo<ListRoleVo> ListByFrom(ListRoleForm form) {
        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotEmpty(form.getRoleName())){
            wrapper.like(SysRole::getRoleName,form.getRoleName());
        }
        if (form.getDisable()!=null){
           wrapper.eq(SysRole::getDisable,form.getDisable());
        }
        wrapper.orderByDesc(SysRole::getCreateTime);
        Page<SysRole> pageSysRole = baseMapper.selectPage((new Page<>(form.getPageNum(), form.getPageSize())),wrapper);

        return objectConvertor.toListRoleVoPage(pageSysRole);
    }

    /**
     * 删除角色
     * @param id 角色id
     * @return
     */
    @Override
    public Boolean del(Integer id) {
        LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(SysRole::getUpdateMemberId,tokenService.getThreadLocalUserId())
                .set(SysRole::getUpdateTime, DateUtil.getSystemTime())
                .eq(SysRole::getId,id);
        // todo  删除会员后mq发短信通知用户
        return remove(wrapper);
    }

    /**
     * 禁用或者启用角色
     * @param form
     * @return
     */
    @Override
    public Boolean updateDisable(UpdateRoleDisableForm form) {
        LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(SysRole::getUpdateMemberId,tokenService.getThreadLocalUserId())
                .set(SysRole::getUpdateTime, DateUtil.getSystemTime())
                .set(SysRole::getDisable,form.getDisable())
                .eq(SysRole::getId,form.getId());
        return update(wrapper);
    }

    /**
     * 修改角色
     * @param form
     * @return
     */
    @Override
    public Boolean updateByFrom(UpdateRoleForm form) {
        LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(SysRole::getUpdateMemberId,tokenService.getThreadLocalUserId())
                .set(SysRole::getUpdateTime, DateUtil.getSystemTime())
                .set(SysRole::getRoleName,form.getRoleName())
                .eq(SysRole::getId,form.getId());
        return update(wrapper);
    }

    /**
     * 查询单个角色信息
     * @param id 角色id
     * @return
     */
    @Override
    public GetRoleDetailVo getGetRoleDetailVoById(Integer id) {
        SysRole sysRole = getById(id);
        return objectConvertor.toGetRoleDetailVo(sysRole);
    }

    /**
     * 查看角色列表
     *
     * @param form
     * @return
     */

    public List<SysRole> lists(PageHelperRequest form) {
        Page<SysRole> page = page(new Page<>(form.getPageNum(), form.getPageSize()));

        List<SysRole> records = page.getRecords();

        return records;
    }
    /**
     * 获取当前登录用户的角色菜单
     * @return
     */
    @Override
    public List<MenuDataItemVo> listRoleBindMenu() {
        Set<Long> sysRoleIds = tokenService.getThreadLocalUser().getSysRoleIds();
        return listRoleMenuIdByCache(sysRoleIds);
    }

    /**
     * 从缓存中获取角色绑定的菜单
     *
     * @param roleIds
     * @return
     */
    private List<MenuDataItemVo> listRoleMenuIdByCache(Set<Long> roleIds) {
        HashOperations<String, String, List<MenuDataItemVo>> hashOps = redisTemplate.opsForHash();
        List<List<MenuDataItemVo>> roleMenuIds = hashOps.multiGet(RedisKeyConstant.ROLE_MENU_PERMISSIONS, roleIds.stream().map(String::valueOf).collect(Collectors.toSet()));
        // 对结果进行处理，将 List<List<Integer>> 转为 List<Integer>
        return roleMenuIds.stream()
                .filter(p -> !CollectionUtils.isEmpty(p))
                .flatMap(List::stream).collect(Collectors.toList());
    }
    /**
     * 角色绑定菜单
     * @param form
     * @return
     */
    @Override
    public Boolean roleBindMenu(RoleBindMenuForm form) {
        if (CollUtil.isNotEmpty(form.getBindMenuIds())){
            List<SysMenu> sysMenuList = sysMenuService.listByIds(form.getBindMenuIds());
            if (form.getBindMenuIds().size()!=sysMenuList.size()){
                throw new BizException("资源非法");
            }
        }
        if (sysRoleBindMenuService.roleBindMenu(form)){
            setSysRoleMenuCache(form.getRoleId());
        }
        return null;
    }

    /**
     * 将所有角色绑定的资源设置到缓存中(通过定时任务触发)
     */
    @Override
    public void setSysRoleMenuCache() {
        PageHelperRequest form = new PageHelperRequest();
        form.setPageNum(1);
        List<SysRole> sysRoles = null;
        List<SysMenu> sysMenus = sysMenuService.getlist();
        while (!CollUtil.isEmpty(sysRoles=lists(form))){
            for (SysRole sysRole : sysRoles) {
                if (sysRole.getDisable() || sysRole.getDelFlag()) {
                    deleteSysRoleMenuCache(sysRole.getId());
                    continue;
                }
                List<Integer> menuIds = sysRoleBindMenuService.listBindMenuIdByRoleId(sysRole.getId());
                List<MenuDataItemVo> menuDataItemVos = listRoleBindMenu(sysRole.getId(), menuIds, sysMenus);
                updateSysRoleMenuCache(sysRole.getId(), menuDataItemVos);
            }
            form.setPageNum(form.getPageNum() + 1);
        }

    }



    @Override
    public void setSysRoleResourceCache() {
        PageHelperRequest form = new PageHelperRequest();
        form.setPageNum(1);
        List<SysRole> sysRoles = null;
        while (!CollectionUtils.isEmpty(sysRoles = lists(form))) {
            for (SysRole sysRole : sysRoles) {
                if (sysRole.getDisable() || sysRole.getDelFlag()) {
                    deleteSysRoleResourceCache(sysRole.getId());
                    continue;
                }
                //查询绑定的资源id列表
                List<Integer> resourceIds = sysRoleBindResourceService.listBindResourceIdByRoleId(sysRole.getId());
                List<SysResource> sysResources = sysResourceService.listByIds(resourceIds);
                updateSysRoleResourceCache(sysRole.getId(),
                        sysResources.stream().map(SysResource::getPath).collect(Collectors.toSet()));
            }
            form.setPageNum(form.getPageNum() + 1);
        }
    }
    /**
     * 更新角色资源路由缓存
     *
     * @param roleId       角色id
     * @param resourcePath 资源路由
     */
    private void updateSysRoleResourceCache(int roleId, Set<String> resourcePath) {
        String cacheKey = RedisKeyConstant.ROLE_RESOURCE_PERMISSIONS;
        HashOperations<String, String, Set<String>> hashOps = redisTemplate.opsForHash();
        hashOps.put(cacheKey, String.valueOf(roleId), resourcePath);
    }

    /**
     * 删除角色资源路由缓存
     *
     * @param roleId 角色id
     */
    private void deleteSysRoleResourceCache(int roleId) {
        String cacheKey = RedisKeyConstant.ROLE_RESOURCE_PERMISSIONS;
        HashOperations<String, String, Set<String>> hashOps = redisTemplate.opsForHash();
        hashOps.delete(cacheKey, String.valueOf(roleId));
    }
    /**
     * 将某个角色绑定的菜单设置到缓存中
     *
     * @param roleId
     */
    private void setSysRoleMenuCache(int roleId) {
        List<SysMenu> sysMenus = sysMenuService.list();
        //查询绑定的资源id列表
        List<Integer> menuIds = sysRoleBindMenuService.listBindMenuIdByRoleId(roleId);
        List<MenuDataItemVo> menuDataItemVos = listRoleBindMenu(roleId, menuIds, sysMenus);
        updateSysRoleMenuCache(roleId, menuDataItemVos);
    }

    /**
     * 查询角色绑定的菜单列表
     *
     * @return
     */
    private List<MenuDataItemVo> listRoleBindMenu(int roleId, List<Integer> menuIds, List<SysMenu> sysMenus) {
        Set<Integer> parentIds = sysMenus.stream().filter(p -> (CommonConstant.ROLE_ADMIN == roleId || menuIds.contains(p.getId()))
                        && p.getPid() > 0)
                .map(SysMenu::getPid).collect(Collectors.toSet());
        //查询所有子节点
        List<SysMenu> parentMenus = sysMenus.stream().filter(p -> parentIds.contains(p.getId())).collect(Collectors.toList());
        List<MenuDataItemVo> parentMenuDataItemVos = objectConvertor.toMenuDataItemVo(parentMenus);
        for (MenuDataItemVo parentMenuDataItemVo : parentMenuDataItemVos) {
            if (parentMenuDataItemVo.getLayout()) {
                parentMenuDataItemVo.setLayout(null);
            }
            List<SysMenu> childMenus = sysMenus.stream().filter(p -> p.getPid().equals(parentMenuDataItemVo.getId())
                            && (CommonConstant.ROLE_ADMIN == roleId || menuIds.contains(p.getId())))
                    .collect(Collectors.toList());
            parentMenuDataItemVo.setRoutes(objectConvertor.toMenuDataItemVo(childMenus).stream().peek(p -> {
                if (p.getLayout()) {
                    p.setLayout(null);
                }
            }).collect(Collectors.toList()));
        }
        return parentMenuDataItemVos;
    }
    /**
     * 删除角色资源路由缓存
     *
     * @param roleId 角色id
     */
    public void deleteSysRoleMenuCache(int roleId) {
        String cacheKey = RedisKeyConstant.ROLE_MENU_PERMISSIONS;
        HashOperations<String, String, List<MenuDataItemVo>> hashOps = redisTemplate.opsForHash();
        hashOps.delete(cacheKey, String.valueOf(roleId));
    }
    /**
     * 更新角色资源路由缓存
     *
     * @param roleId          角色id
     * @param menuDataItemVos 资源路由
     */
    public void updateSysRoleMenuCache(int roleId, List<MenuDataItemVo> menuDataItemVos) {
        String cacheKey = RedisKeyConstant.ROLE_MENU_PERMISSIONS;
        HashOperations<String, String, List<MenuDataItemVo>> hashOps = redisTemplate.opsForHash();
        hashOps.put(cacheKey, String.valueOf(roleId), menuDataItemVos);

    }

    /**
     * 角色绑定资源列表
     *
     * @param form
     * @return
     */
    @Override
    public Boolean roleBindResource(RoleBindResourceForm form) {
        if (!CollectionUtils.isEmpty(form.getBindResourceIds())) {
            List<SysResource> sysResources = sysResourceService.listByIds(form.getBindResourceIds());
            if (form.getBindResourceIds().size() != sysResources.size()) {
                throw new BizException("资源非法");
            }
        }
        if (sysRoleBindResourceService.roleBindResource(form)) {
            setSysRoleResourceCache(form.getRoleId());
        }
        return true;
    }

    /**
     * 将某个角色绑定的资源设置到缓存中
     *
     * @param roleId
     */
    private void setSysRoleResourceCache(int roleId) {
        //查询绑定的资源id列表
        List<Integer> resourceIds = sysRoleBindResourceService.listBindResourceIdByRoleId(roleId);
        List<SysResource> sysResources = sysResourceService.listByIds(resourceIds);
        if (CollectionUtils.isEmpty(sysResources)) {
            updateSysRoleResourceCache(roleId, new HashSet<>());
        } else {
            updateSysRoleResourceCache(roleId,
                    sysResources.stream().map(SysResource::getPath).collect(Collectors.toSet()));
        }
    }

}
